package com.codejoys.monitor.service.impl;

import com.codejoys.monitor.Constants;
import com.codejoys.monitor.Utils;
import com.codejoys.monitor.config.SysConfig;
import com.codejoys.monitor.dao.DeviceDao;
import com.codejoys.monitor.domain.ChannelConfig;
import com.codejoys.monitor.domain.SysConfigExample;
import com.codejoys.monitor.domain.VoltageModeConfig;
import com.codejoys.monitor.dto.ChannelConfigMap;
import com.codejoys.monitor.dto.DeviceDto;
import com.codejoys.monitor.dto.SystemStatus;
import com.codejoys.monitor.event.AlarmEvent;
import com.codejoys.monitor.event.CheckTemperatureEvent;
import com.codejoys.monitor.exception.SysException;
import com.codejoys.monitor.jna.CANLib;
import com.codejoys.monitor.mapper.DeviceDataMapper;
import com.codejoys.monitor.mapper.DeviceMapper;
import com.codejoys.monitor.mapper.SysConfigMapper;
import com.codejoys.monitor.service.ModbusService;
import org.omg.CORBA.SystemException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;

@Service
public class ModbusServiceImpl implements ModbusService {
    private static final Logger logger = LoggerFactory.getLogger(ModbusServiceImpl.class);

    @Autowired
    SysConfig sysConfig;

    @Autowired
    CANLib canLib;

    @Autowired
    DeviceDao deviceDao;

    @Autowired
    DeviceDataMapper deviceDataMapper;

    @Autowired
    DeviceMapper deviceMapper;

    @Autowired
    ChannelConfigMap configMap;

    @Autowired
    SysConfigMapper sysConfigMapper;

    ExecutorService threadPool = Executors.newFixedThreadPool(8);

    @Override
    public synchronized int startPower(DeviceDto deviceDto) {
        if (deviceDto.getStatus() == Constants.Status.AGING.value()) {
            logger.warn("设备已经启动，不能再次开启。。。");
            return -1;
        }

        logger.info("开启设备：{}", deviceDto);

        try (Socket socket = ensureSocket(deviceDto.getChannel())) {
            startOne(socket, deviceDto);

        } catch (IOException e) {
            logger.error("开启设备失败！", e);

            throw new RuntimeException("开启设备失败：" + e.getMessage());
        }

        return 0;
    }

    public Socket ensureSocket(String channel) throws IOException {
        // 通道对应的ip地址
        ChannelConfig config = configMap.getConfigs().get(channel);
        String ip = config.getCabinetIp();
        if (ip == null) {
            throw new RuntimeException("通道没有配置对应的ip:" + channel);
        }
        logger.info("通道对应的ip：{}", ip);

        return connect(ip);
    }

    public static Socket connect(String ip) throws IOException {
        logger.info("开始连接socket。。。");
        Socket socket = new Socket();
        socket.setSoTimeout(2000);  // read timeout, not connect time out
        socket.setKeepAlive(true);
        socket.connect(new InetSocketAddress(ip, Integer.valueOf(502)), 2000);

        logger.info("socket连接成功！");
        return socket;
    }

    @Override
    public synchronized void startAll(List<DeviceDto> devices) {
        logger.info("准备开启的设备：{}", devices);
        List<DeviceDto> devicesToStart = devices.stream()
                .filter(d -> d.getStatus() == 1)        // 防止选中已开启的设备
                .collect(Collectors.toList());

        logger.info("可以开启的设备：{}", devicesToStart);

        final Map<String, ChannelConfig> configs = configMap.getConfigs();
        // 根据设备通道对应的socket(ip)，分成不同的组
        final Map<String, List<DeviceDto>> group = devicesToStart.stream()
                .collect(Collectors.groupingBy(dto -> {
                    return configs.get(dto.getChannel()).getCabinetIp();
                }));

        group.forEach((ip, deviceDtos) -> {
            threadPool.execute(() -> openGroup(ip, deviceDtos));
        });
    }

    @Override
    public synchronized void stopAll(List<DeviceDto> devices) {
        logger.info("准备停止的设备：{}", devices);
        List<DeviceDto> devicesToStop = devices.stream()
                .filter(d -> d.getStatus() == 2)        // 只停止老化中的设备
                .collect(Collectors.toList());

        logger.info("可以停止的设备：{}", devicesToStop);

        final Map<String, ChannelConfig> configs = configMap.getConfigs();
        // 根据设备通道对应的socket(ip)，分成不同的组
        final Map<String, List<DeviceDto>> group = devicesToStop.stream()
                .collect(Collectors.groupingBy(dto -> {
                    return configs.get(dto.getChannel()).getCabinetIp();
                }));

        group.forEach((ip, deviceDtos) -> {
            threadPool.execute(() -> stopGroup(ip, deviceDtos));
        });
    }

    private void stopGroup(String ip, List<DeviceDto> deviceDtos) {
        deviceDtos.forEach(d -> {
            try (Socket socket = connect(ip)) {
                stopOne(socket, d);
            } catch (IOException e) {
                logger.error("socket连接出错！", e);

                throw new SysException(2003, "socket连接出错！一键停止失败。");
            }
        });
    }

    private void stopOne(Socket socket, DeviceDto d) throws IOException {
        logger.info("关闭设备。{}", d);

        String channel = d.getChannel();
        ChannelConfig config = configMap.getConfigs().get(channel);
        if (config == null) {
            throw new RuntimeException("通道的CAN地址未配置：" + channel);
        }

        int retry = 1;
        int result = 0;

        do {
            // 模块断电
            result = canLib.TurnOnOffModule(config.getCanIndex(), 0, (byte) 1, config.getCanModuleId(), 0);

            if (result != 1) {
                logger.error("模块关闭失败！1秒后重试。。{} result: {}", channel, result);
                Utils.trySleep(1000);
            } else {
                logger.info("设备关闭成功！");
                break;
            }

            retry++;

            // 最多5次重试
            if (retry > 5) {
                logger.error("5次重试关闭失败，退出。。。");
                break;
            }
        } while (result != 1);

        toggleOpen(socket, config, false);   // 关闭电源

        // 老化中的设备，改成待下机状态，其他状态，只执行关机操作，不改设备状态
        if (d.getStatus() == Constants.Status.AGING.value()) {
            // 更新设备状态
            if (d.getAgingEndTime().getTime() < new Date().getTime()) {
                d.setStatus((byte) Constants.Status.FINISHED.value()); // 老化完成
            } else {
                d.setStatus((byte) Constants.Status.INTERRUPT.value()); // 老化终止
            }
            d.setAgingStopTime(new Date());
            deviceMapper.updateByPrimaryKeySelective(d);
        }
    }

    private void openGroup(String ip, List<DeviceDto> deviceDtos) {
        deviceDtos.forEach(d -> {
            try (Socket socket = connect(ip)) {
                startOne(socket, d);
            } catch (IOException e) {
                logger.error("启动设备失败：{}", d);
                logger.error("错误信息：", e);

                throw new SysException(2002, "socket连接出错！一键开启失败。");
            }
        });
    }

    private void startOne(Socket socket, DeviceDto device) throws IOException {
        String channel = device.getChannel();
        ChannelConfig config = configMap.getConfigs().get(channel);

        final VoltageModeConfig voltageModeConfig = deviceDao.findVoltageModeConfig(
                device.getModel(), device.getChannelId());

        toggleOpen(socket, config, true);   // 打开电源

        // 等待设备供电
        try {
            Thread.sleep(6000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        if (config == null) {
            throw new RuntimeException("通道的CAN地址未配置：" + channel);
        }

        logger.info("待老化的设备：{}", device);

        // 开机重试次数
        int retry = 1;
        int result = 0; // 开启成功
        do {
            // 设置电流电压
            final int v = canLib.SetModuleVoltage(
                    config.getCanIndex(), 0, (byte) 1,
                    config.getCanModuleId(), device.getPresetVoltage().doubleValue());

            final int c = canLib.SetModuleCurrent(
                    config.getCanIndex(), 0, (byte) 1,
                    config.getCanModuleId(), device.getPresetCurrent().doubleValue());

            logger.info("设置电流电压结果：{}, {}", Integer.toHexString(v), Integer.toHexString(c));

            // 设置低压模式（默认高压）
            // 注意：并不是所有的设备型号都设置了高低压模式
            // 数据库里面1：高压模式   2：低压模式
            if (voltageModeConfig != null && voltageModeConfig.getHiLoMode() == 2) {
                final int m = canLib.SetModuleHiLoVoltage(
                        config.getCanIndex(), 0, (byte) 1,
                        config.getCanModuleId(), 0);

                logger.info("设置低压模式结果：{}", Integer.toHexString(m));
            }

            // 模块供电
            result = canLib.TurnOnOffModule(config.getCanIndex(), 0, (byte) 1, config.getCanModuleId(), 1);

            if (result != 0) {
                Utils.trySleep(1000);
                logger.error("第{}次模块开启失败！{} result: {}", retry, channel, result);
            } else {
                break;
            }

            retry++;

            // 最多5次重试
            if (retry > 5) {
                logger.error("5次重试启动失败，退出。。。");
                return;
            }
        } while (result != 0);

        logger.info("模块开启成功！");

        // 更新设备状态、开启时间
        device.setStatus((byte) Constants.Status.AGING.value()); // 老化中

        Date now = new Date();
        device.setAgingStartTime(now);   // 老化开启时间
        device.setAgingEndTime(new Date(now.getTime() + device.getPresetDuration() * 1000));   // 老化结束时间
        device.setAgingStopTime(null);
        deviceMapper.updateByPrimaryKey(device);

        logger.info("更新设备成功！");
    }

    @Override
    public synchronized int stopPower(DeviceDto deviceDto) {
        logger.info("关闭设备。{}", deviceDto);

        String channel = deviceDto.getChannel();
        try (Socket socket = ensureSocket(channel)) {
            stopOne(socket, deviceDto);
        } catch (IOException e) {
            logger.error("关闭设备失败！", e);

            throw new RuntimeException("关闭设备失败：" + e.getMessage());
        }

        return 0;
    }

    @Override
    public int stopPowerOnStart(DeviceDto deviceDto) {
        logger.info("启动时关闭设备。{}", deviceDto);

        String channel = deviceDto.getChannel();
        try (Socket socket = ensureSocket(channel)) {
            ChannelConfig config = configMap.getConfigs().get(channel);
            if (config == null) {
                throw new RuntimeException("通道的CAN地址未配置：" + channel);
            }

            toggleOpen(socket, config, false);   // 关闭电源
        } catch (IOException e) {
            logger.error("关闭设备失败！", e);

            throw new RuntimeException("关闭设备失败：" + e.getMessage());
        }

        return 0;
    }

    @Override
    public List<Integer> readTemperature() {
        return null;
    }

    @Override
    public int sendModuleTemperature(String channel, int temperature) {
        return 0;
    }

    @Override
    public int sendAlarm(DeviceDto device) {
        logger.info("发送告警：{} {}", device.getSn(), device.getChannel());

        // 查询报警设置是否开启
        SysConfigExample example = new SysConfigExample();
        example.createCriteria().andNameEqualTo(Constants.CONFIG_KEY_IS_ALARM_ON);
        List<com.codejoys.monitor.domain.SysConfig> configs = sysConfigMapper.selectByExample(example);

        boolean isAlarmOn = (configs != null && configs.size() > 0
                && Integer.parseInt(configs.get(0).getValue()) == Constants.CONFIG_VALUE_ALARM_ON);

        if (!isAlarmOn) {
            logger.info("报警开关未开启！");
            return 0;
        }

        String channel = device.getChannel();
        ChannelConfig config = configMap.getConfigs().get(channel);

        // 开启单个机柜的报警
        toggleAlarmForIp(config.getCabinetIp(), true);

        return 0;
    }

    @Override
    public int stopAlarm() {
        logger.info("取消告警：");

        // 关闭所有机柜的报警
        final Set<String> ips = configMap.getConfigs().entrySet()
                .stream().map(entry -> entry.getValue().getCabinetIp())
                .collect(Collectors.toSet());

        logger.info("关闭报警：{}", ips);

        ips.forEach(ip -> toggleAlarmForIp(ip, false));

        return 0;
    }

    public static void toggleAlarmForIp(String ip, boolean isOpen) {
        // 发警报需要知道通道号，1-18 和 19-36 归属不同的ip
        try (Socket socket = connect(ip)) {
            byte[] sendData = {
                    0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, // 前7个字节固定
                    0x06,       // 操作码
                    0x00, 29,   // 地址
                    isOpen ? (byte) 0xff : (byte) 0x00,         // 打开报警
                    isOpen ? (byte) 0xff : (byte) 0x00,         // 取消报警
            };

            socket.getOutputStream().write(sendData);
            socket.getOutputStream().flush();

            try {
                Thread.sleep(20);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            logger.info("发送数据成功！等待结果。。。");

            byte[] resultData = new byte[100];
            int read = socket.getInputStream().read(resultData, 0, 100);
            String result = formatBytes(resultData, read);

            logger.debug("响应数据：{}", result);
        } catch (IOException e) {
            logger.error("socket连接出错！发送告警失败！", e);
        }
    }

    @Override
    public List<Boolean> readPowerState(String cabinet) {
        String ip = getCabinetIp(cabinet);

        if (ip == null) return new ArrayList<>();

        try (Socket socket = connect(ip)) {
            byte[] sendData = {
                    0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, // 前7个字节固定
                    0x02,           // 操作码
                    0x00, 0x00,     // 地址
                    0x00, 0x18      // 3号房16，4号房18（表示取数据个数）
            };

            socket.getOutputStream().write(sendData);
            socket.getOutputStream().flush();

            try {
                Thread.sleep(20);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            logger.info("发送数据成功！等待结果。。。");

            byte[] resultData = new byte[200];
            int read = socket.getInputStream().read(resultData, 0, 200);
            if (read < 12) {
                logger.error("响应字节数据异常！{}", read);
                return new ArrayList<>();
            }

            logger.info("响应数据：{} {} {}",
                    Integer.toBinaryString(resultData[9]),
                    Integer.toBinaryString(resultData[10]),
                    Integer.toBinaryString(resultData[11])
            );

            // 返回数据示例 00 00 00 00 | 00 06 01 02  | 03 54 AB 02
            // 前8个字节固定，第9个字节3，表示后续3个字节存放结果，低 -> 高
            // resultData[9]:  第10个字节表示0 - 7  号模块开关状态
            // resultData[10]: 第11个字节表示8 - 15 号模块开关状态
            // resultData[11]: 第12个字节表示16 - 17 号模块开关状态（3号房需要，4号房只需要两个字节）

            boolean[] marks = new boolean[18];
            for (int i = 0; i < 8; i++) {
                int mask = 0x1 << i;
                marks[i] = ((resultData[9] & mask) == mask);        // 相等表示闭合
                marks[i + 8] = ((resultData[10] & mask) == mask);
            }

            marks[16] = ((resultData[11] & 0x01) == 0x01);
            marks[17] = ((resultData[11] & 0x02) == 0x02);

            logger.info("开关结果：\n{}", marks);

            List<Boolean> result = new ArrayList<>();

            for (boolean m : marks) {
                result.add(m);
            }
            return result;
        } catch (IOException e) {
            logger.error("读取开关状态异常：", e);
            return new ArrayList<>();
        }
    }

    @Override
    public List<String> getTemperatures(String cabinet) {
        return getTprs(cabinet, true);
    }

    private String getCabinetIp(String cabinet) {
        final Set<String> ips = configMap.getConfigs().entrySet().stream()
                .filter(entry -> Objects.equals(entry.getValue().getCabinet(), cabinet))
                .map(entry -> entry.getValue().getCabinetIp())
                .collect(Collectors.toSet());

        logger.info("获取温度, 机柜地址： {}", ips);
        if (ips == null || ips.size() == 0) {
            logger.warn("未找到对应的机柜地址！{}", cabinet);
            return null;
        }

        String ip = ips.iterator().next();
        logger.info("ip {}", ip);

        return ip;
    }

    public List<String> getTprs(String cabinet, boolean supressError) {
        String ip = getCabinetIp(cabinet);

        if (ip == null) return new ArrayList<>();

        // 发警报需要知道通道号，1-18 和 19-36 归属不同的ip
        try (Socket socket = connect(ip)) {
            byte[] sendData = {
                    0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, // 前7个字节固定
                    0x03,           // 操作码
                    0x00, 0x00,     // 地址
                    0x00, 0x04
            };

            socket.getOutputStream().write(sendData);
            socket.getOutputStream().flush();

            try {
                Thread.sleep(20);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            logger.info("发送数据成功！等待结果。。。");

            byte[] resultData = new byte[200];
            int read = socket.getInputStream().read(resultData, 0, 200);
            String result = formatBytes(resultData, read);
            logger.debug("响应数据：{}", result);

            double tA = ((resultData[9]  << 8) | (resultData[10] & 0x0000_00FF)) / 10.0;
            double tB = ((resultData[11] << 8) | (resultData[12] & 0x0000_00FF)) / 10.0;
            double tC = ((resultData[13] << 8) | (resultData[14] & 0x0000_00FF)) / 10.0;
            double tD = ((resultData[15] << 8) | (resultData[16] & 0x0000_00FF)) / 10.0;

            return Arrays.asList(
                    String.valueOf(tA),
                    String.valueOf(tB),
                    String.valueOf(tC),
                    String.valueOf(tD));
        } catch (IOException e) {
            logger.error("socket连接出错！发送告警失败！", e);

            if (!supressError) {
                throw new SysException(e);
            }
        }

        return new ArrayList<>();
    }

    @Override
    public SystemStatus checkSystemStatus() {
        SystemStatus status = new SystemStatus();

        // 检查modbus状态
        status.setCabinetStatusList(checkCabinetStatus());

        // 检查CAN状态
        status.setCanStatusList(checkCanStatus());

        return status;
    }

    private List<SystemStatus.CanStatus> checkCanStatus() {
        logger.info("开始检查CAN设备状态！");
        List<SystemStatus.CanStatus> result = new ArrayList<>();

        List<String> channels = new ArrayList<String>() {{
            add("A0301");
            add("A0401");
        }};

        for (String channel : channels) {
            SystemStatus.CanStatus status = new SystemStatus.CanStatus();

            final ChannelConfig config = configMap.getConfigs().get(channel);
            int canIndex = config.getCanIndex();
            int moduleId = config.getCanModuleId();

            logger.info("检查通道：{} canIndex：{} 模块id {}", channel, canIndex, moduleId);
            status.setCanIndex(canIndex);
            try {
                canLib.GetModuleStatus(canIndex, 0, 1, moduleId);
                status.setConnected(true);
            } catch (Throwable e) {
                status.setConnected(false);
            }

            result.add(status);
        }

        return result;
    }

    private List<SystemStatus.CabinetStatus> checkCabinetStatus() {
        logger.info("开始检查电源柜状态！");
        List<SystemStatus.CabinetStatus> result = new ArrayList<>();

        List<String> cabinets = new ArrayList<String>() {{
            add("A03X");
            add("A03Y");
            add("A04X");
            add("A04Y");
        }};

        for (String cabinet : cabinets) {
            SystemStatus.CabinetStatus status = new SystemStatus.CabinetStatus();
            logger.info("检查电源柜：{}", cabinet);
            status.setCabinet(cabinet);

            try {
                getTprs(cabinet, false);

                status.setConnected(true);
                status.setReadWriteNormal(true);
            } catch (SysException e) {
                if (e.getCause() instanceof SocketTimeoutException) {
                    status.setConnected(false);
                    status.setReadWriteNormal(false);
                } else {
                    status.setConnected(true);
                    status.setReadWriteNormal(false);
                }
            }

            result.add(status);
        }

        return result;
    }

    // 打开或者关闭电源
    public static void toggleOpen(Socket socket, ChannelConfig channelConfig, boolean open) throws IOException {
        byte address = (byte) channelConfig.getModbusIndex().intValue();
        byte mId = open ? address : (byte) (address + 20);// 开关区别在于地址不一样

        byte[] sendData = {
                0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 0x05,     // 前8个字节固定
                0x00, mId,     // 9，10字节表示地址，从00开始
                (byte) 0xff, 0x00    // 0xFF表示按下，00表示松开
        };

        socket.getOutputStream().write(sendData);
        socket.getOutputStream().flush();

        try {
            Thread.sleep(20);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 读取第一次响应
        byte[] resultData = new byte[100];
        int read = socket.getInputStream().read(resultData, 0, 100);
        String result = formatBytes(resultData, read);

        try {
            Thread.sleep(20);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        byte[] resetData = {
                0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 0x05,     // 前8个字节固定
                0x00, mId,
                0x00, 0x00
        };
        socket.getOutputStream().write(resetData);
        socket.getOutputStream().flush();

        logger.info("发送数据成功！等待结果。。。");

        // 读第二个命令的结果
        try {
            Thread.sleep(20);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        resultData = new byte[100];
        read = socket.getInputStream().read(resultData, 0, 100);
        result = result + " / " + formatBytes(resultData, read);

        logger.info("服务器响应：{}", result);
    }

    private static String formatBytes(byte[] bytes, int read) {
        if (read >= bytes.length) {
            read = bytes.length;
        }

        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < read; i++) {
            sb.append(String.format("%02X", bytes[i]));
        }

        return sb.toString();
    }

    @EventListener
    public void onApplicationEvent(AlarmEvent event) {
        logger.info("收到告警消息：{}", event.getDevice());

        this.sendAlarm(event.getDevice());
    }

    @EventListener
    public void onApplicationEvent(CheckTemperatureEvent event) {
        logger.info("检查温度数据。。。");

        // 查询报警设置是否开启
        List<com.codejoys.monitor.domain.SysConfig> configs = sysConfigMapper.selectByExample(null);
        if (configs == null || configs.size() == 0) return;

        // config name -> value
        final Map<String, String> sysConfigMap = configs.stream()
                .collect(Collectors.toMap(com.codejoys.monitor.domain.SysConfig::getName, c -> c.getValue()));

        if (sysConfigMap.containsKey(Constants.CONFIG_KEY_IS_ALARM_ON)) {
            String value = sysConfigMap.get(Constants.CONFIG_KEY_IS_ALARM_ON);
            boolean isAlarmOn = Integer.parseInt(value) == Constants.CONFIG_VALUE_ALARM_ON;

            if (!isAlarmOn) {
                logger.info("检查温度上限 - 报警未打开！");
                return;
            }
        }

        double tprUpper = 0;
        double cabinetTprUpper = 0;
        if (sysConfigMap.containsKey(Constants.CONFIG_KEY_TPR_UPPER)) {
            String upper = sysConfigMap.get(Constants.CONFIG_KEY_TPR_UPPER);

            if (!StringUtils.isEmpty(upper)) {
                tprUpper = Double.parseDouble(upper);
            }
        }

        if (sysConfigMap.containsKey(Constants.CONFIG_KEY_CABINET_TPR_UPPER)) {
            String upper = sysConfigMap.get(Constants.CONFIG_KEY_CABINET_TPR_UPPER);

            if (!StringUtils.isEmpty(upper)) {
                cabinetTprUpper = Double.parseDouble(upper);
            }
        }

        List<String> cabinets = new ArrayList<String>(){{
            add("A03X");
            add("A03Y");
            add("A04X");
            add("A04Y");
        }};

        // cabinet -> ip
        final Map<String, String> cabinetConfigMap = new HashMap<>();
        configMap.getConfigs().entrySet()
            .stream()
            .map(entry -> entry.getValue())
            .forEach(config -> {
                cabinetConfigMap.put(config.getCabinet(), config.getCabinetIp());
            });

        logger.info("cabinet -> ip: {}", cabinetConfigMap);

        for (String cabinet : cabinets) {
            final List<String> tprs = getTemperatures(cabinet);

            if (tprs.size() != 4) {
                logger.warn("温度数据错误：{}", tprs);
                continue;
            }

            int houseTprIndex = getHouseTprIndex(cabinet);
            for (int i = 0; i < tprs.size(); i++) {
                boolean isHouseTpr = (i == houseTprIndex);
                double tpr = Double.parseDouble(tprs.get(i));
                if (isHouseTpr
                        && tpr > 0
                        && tprUpper > 0
                        && tpr > tprUpper
                ) {
                    logger.warn("老化房温度【{}】超过上限【{}】", tpr, tprUpper);

                    if (cabinetConfigMap.containsKey(cabinet)) {
                        toggleAlarmForIp(cabinetConfigMap.get(cabinet), true);
                    }
                }

                if (!isHouseTpr
                        && tpr > 0
                        && cabinetTprUpper > 0
                        && tpr > cabinetTprUpper
                ) {
                    logger.warn("电源柜温度【{}】超过上限【{}】", tpr, cabinetTprUpper);
                    if (cabinetConfigMap.containsKey(cabinet)) {
                        toggleAlarmForIp(cabinetConfigMap.get(cabinet), true);
                    }
                }
            }
        }
    }

    // A03X - [4]  - 1号房
    // A03Y - [3]  - 2号房
    // A04X - [4]  - 3号房
    // A04Y - [1]  - 4号房
    // 判断温度是电源柜的，还是老化房的，返回四个温度中老化房温度的索引
    private static int getHouseTprIndex(String cabinet) {
        if ("A03X".equalsIgnoreCase(cabinet)) {
            return 3;
        }

        if ("A03Y".equalsIgnoreCase(cabinet)) {
            return 2;
        }

        if ("A04X".equalsIgnoreCase(cabinet)) {
            return 3;
        }

        if ("A04Y".equalsIgnoreCase(cabinet)) {
            return 0;
        }

        logger.warn("未知的电源柜配置：{}", cabinet);
        return -1;
    }
}
